#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "b_ring_buffer.h"

ringbuffer_t *ringbuffer_init(size_t size) {;
    ringbuffer_t *rb = malloc(sizeof(ringbuffer_t));
    if (rb == NULL) return NULL;
    rb->buffer = malloc(size);
    if (rb->buffer == NULL) {
        free(rb);
        return NULL;
    }
    rb->total_size = size;
    rb->head = 0;
    rb->tail = 0;
    rb->free_size = size;
    pthread_mutex_init(&rb->lock, NULL);
    //pthread_cond_init(&rb->not_empty, NULL);
    //pthread_cond_init(&rb->not_full, NULL);
    return rb;
}

void ringbuffer_free(ringbuffer_t *rb) {
    if (rb != NULL) {
        free(rb->buffer);
        free(rb);
    }
}

size_t ringbuffer_write(ringbuffer_t *rb, const void *data, size_t len) {
    size_t bytes_to_end = rb->total_size - rb->tail;
    size_t bytes_to_write = (len > bytes_to_end) ? bytes_to_end : len;
    pthread_mutex_lock(&rb->lock);
    memcpy(rb->buffer + rb->tail, data, bytes_to_write);
    if (bytes_to_write < len) {
        memcpy(rb->buffer, (const unsigned char *)data + bytes_to_write, len - bytes_to_write);
    }
    rb->tail = (rb->tail + len) % rb->total_size;
    rb->free_size -= len;
    pthread_mutex_unlock(&rb->lock);
    //(&rb->not_full);
    return len;
}

size_t ringbuffer_read(ringbuffer_t *rb, void *data, size_t len) {
    size_t bytes_to_end = rb->total_size - rb->head;
    size_t bytes_to_read = (len > bytes_to_end) ? bytes_to_end : len;
    pthread_mutex_lock(&rb->lock);

    memcpy(data, rb->buffer + rb->head, bytes_to_read);
    if (bytes_to_read < len) {
        memcpy((unsigned char *)data + bytes_to_read, rb->buffer, len - bytes_to_read);
    }
    rb->head = (rb->head + len) % rb->total_size;
    rb->free_size += len;
    pthread_mutex_unlock(&rb->lock);
    //pthread_cond_signal(&rb->not_empty);
    return len;
}

size_t ringbuffer_available_to_read(ringbuffer_t *rb) {
    size_t size;
    size = rb->total_size - rb->free_size;
    return size;
}

size_t ringbuffer_available_to_write(ringbuffer_t *rb) {
    return rb->free_size;
}